﻿--Macaroon, a World of Warcraft® user interface addon.
--Copyright© 2008 Connor H. Chenoweth, aka Maul

local currentVersion = 30000.281

Macaroon = {

	SlashCommands = {},
	SlashHelp = {},
	spellIndex = {},
	macroIconIndex = {},
	companionIndex = {},
	Ypos = 90,
	Panels = {},
	maxActionID = 132,
	maxPetID = 10,
}

Macaroon.ShowGrids = {}

Macaroon.HideGrids = {}

MacaroonSharedSavedState = {}

MacaroonSavedState = {
	buttonLoc = { -0.85, -111.45 },
	buttonRadius = 87.5,
	firstRun = true,
	snapToTol = 28,
	registerForClicks = "AnyUp",
	options = {},
	checkButtons = {
		[101] = 1,
		[102] = 1,
		[104] = 1,
		[105] = 1,
		[106] = 1,
		[201] = 1,
	},
	bfskin = {},
	savedVersion = 0,
	bagOffsetX = 0,
	bagOffsetY = 70,
	bagScale = 1,
	cooldownAlpha = 1,
	autocastAlpha = 1,
	fadeSpeed = 0.5,
	selfCast = false,
	focusCast = false,
	rightClickTarget = false,
	freeSlots = 16,
	useShared = false,
	sharedProfile = false,
}

Macaroon.StatesToSave = {}

Macaroon.SavedDataLoad = {}

Macaroon.SavedDataUpdate = {}

Macaroon.SetSavedVars = {}

MacaroonMacroMaster = {}

MacaroonItemCache = {}

local specialStringsUpdated, load, ss, playerEnteredWorld

local lower = string.lower
local match = string.match
local find = string.find
local format = string.format
local floor = math.floor

local MACAROON_STRINGS = MACAROON_STRINGS

local function clearTable(table)

	if (table) then
		for k in pairs(table) do
			table[k] = nil
		end
	end
end

Macaroon.clearTable = clearTable

local function copyTable(table)

	if (table == nil) then
		return
	end

	if (type(table) ~= "table") then
		return
	end

	local data = {}

	for k, v in pairs (table) do

		if (type(v) == "table") then
			data[k] = copyTable(v)
		else
			data[k] = v
		end
	end

	return data
end

Macaroon.copyTable = copyTable

local defaultSavedState = copyTable(MacaroonSavedState)

local function getChildrenAndRegions(frame)

	if (frame == nil) then
		return
	end

	local data, childData = {}, {}
	local children, regions = { frame:GetChildren() }, { frame:GetRegions() }

	for k,v in pairs(children) do

		tinsert(data, v:GetName())

		childData = getChildrenAndRegions(v)

		for key,value in pairs(childData) do
			tinsert(data, value)
		end
	end

	for k,v in pairs(regions) do
		tinsert(data, v:GetName())
	end

	return data
end

Macaroon.getChildrenAndRegions = getChildrenAndRegions

local function updateSpellIndex(update)

	if (not update) then
		clearTable(Macaroon.spellIndex)
	end

	local spellName, spellRank
	local i = 1

	repeat
		spellName, spellRank = GetSpellName(i, BOOKTYPE_SPELL)

   		if (spellName) then

   			spellName = lower(spellName)
   			spellRank = lower(spellRank)

   			local rank = tonumber(match(spellRank, "%d+")) or 99

   			if (update) then

   				if (not Macaroon.spellIndex[spellName.."("..spellRank..")"]) then
   					Macaroon.spellIndex[spellName.."("..spellRank..")"] = { i, rank, spellRank, BOOKTYPE_SPELL }
   				end

   				if (not Macaroon.spellIndex[spellName]) then
   					Macaroon.spellIndex[spellName] = { i, rank, spellRank, BOOKTYPE_SPELL }
   				end

   				if (not Macaroon.spellIndex[spellName.."()"]) then
   					Macaroon.spellIndex[spellName.."()"] = { i, rank, spellRank, BOOKTYPE_SPELL }
   				end

   			else

				Macaroon.spellIndex[spellName.."("..spellRank..")"] = { i, rank, spellRank, BOOKTYPE_SPELL }

				if (not Macaroon.spellIndex[spellName]) then
					Macaroon.spellIndex[spellName] = { i, rank, spellRank, BOOKTYPE_SPELL }
				elseif (rank > Macaroon.spellIndex[spellName][2]) then
					Macaroon.spellIndex[spellName] = { i, rank, spellRank, BOOKTYPE_SPELL }
				end

				if (not Macaroon.spellIndex[spellName.."()"]) then
					Macaroon.spellIndex[spellName.."()"] = { i, rank, spellRank, BOOKTYPE_SPELL }
				elseif (rank > Macaroon.spellIndex[spellName.."()"][2]) then
					Macaroon.spellIndex[spellName.."()"] = { i, rank, spellRank, BOOKTYPE_SPELL }
				end
			end

   		end

   		i = i + 1

   	until (not spellName)

	i = 1

	repeat
		spellName, spellRank = GetSpellName(i, BOOKTYPE_PET)

   		if (spellName) then

   			spellName = lower(spellName)
   			spellRank = lower(spellRank)

   			local rank = tonumber(match(spellRank, "%d+")) or 0

   			if (update) then

   				if (not Macaroon.spellIndex[spellName.."("..spellRank..")"]) then
   					Macaroon.spellIndex[spellName.."("..spellRank..")"] = { i, rank, spellRank, BOOKTYPE_PET }
   				end

   				if (not Macaroon.spellIndex[spellName]) then
   					Macaroon.spellIndex[spellName] = { i, rank, spellRank, BOOKTYPE_PET }
   				end

   				if (not Macaroon.spellIndex[spellName.."()"]) then
   					Macaroon.spellIndex[spellName.."()"] = { i, rank, spellRank, BOOKTYPE_PET }
   				end

   			else

				Macaroon.spellIndex[spellName.."("..spellRank..")"] = { i, rank, spellRank, BOOKTYPE_PET }

				if (not Macaroon.spellIndex[spellName]) then
					Macaroon.spellIndex[spellName] = { i, rank, spellRank, BOOKTYPE_PET }
				elseif (rank > Macaroon.spellIndex[spellName][2]) then
					Macaroon.spellIndex[spellName] = { i, rank, spellRank, BOOKTYPE_PET }
				end

				if (not Macaroon.spellIndex[spellName.."()"]) then
					Macaroon.spellIndex[spellName.."()"] = { i, rank, spellRank, BOOKTYPE_PET }
				elseif (rank > Macaroon.spellIndex[spellName.."()"][2]) then
					Macaroon.spellIndex[spellName.."()"] = { i, rank, spellRank, BOOKTYPE_PET }
				end
			end
   		end

   		i = i + 1

   	until (not spellName)
end

local function updateMacroIconIndex()

	local texture

	clearTable(Macaroon.macroIconIndex)

	for i=1,GetNumMacroIcons() do
		Macaroon.macroIconIndex[GetMacroIconInfo(i)] = i
	end
end

local function updateShapeshiftStrings()

	if (UnitClass("player") == MACAROON_STRINGS.DRUID or
	    UnitClass("player") == MACAROON_STRINGS.PRIEST or
	    UnitClass("player") == MACAROON_STRINGS.ROGUE or
	    UnitClass("player") == MACAROON_STRINGS.WARRIOR or
	    UnitClass("player") == MACAROON_STRINGS.WARLOCK) then

		local _, name

		for i=1,GetNumShapeshiftForms() do

			_, name = GetShapeshiftFormInfo(i)

			if (name) then
				MACAROON_STRINGS.STATES["stance"..i] = name
			end
		end

		if (not specialStringsUpdated) then

			if (UnitClass("player") == MACAROON_STRINGS.DRUID) then

				local origString, index, nextString = MACAROON_STRINGS.DRUID_PROWL, 1

				while (MACAROON_STRINGS["BARSTATE_"..index]) do
					index = index + 1
				end

				MACAROON_STRINGS.STATES.stance0 = MACAROON_STRINGS.DRUID_STANCE0
				MACAROON_STRINGS.STATES.stance8 = MACAROON_STRINGS.DRUID_PROWL

				index = 3

				while (MACAROON_STRINGS["BARSTATE_"..index]) do

					nextString = MACAROON_STRINGS["BARSTATE_"..index]

					MACAROON_STRINGS["BARSTATE_"..index] = origString

					origString = nextString

					index = index + 1
				end

				MACAROON_STRINGS["BARSTATE_"..index] = origString

			elseif (UnitClass("player") == MACAROON_STRINGS.PRIEST) then

				MACAROON_STRINGS.STATES.stance0 = MACAROON_STRINGS.PRIEST_HEALER

			elseif (UnitClass("player") == MACAROON_STRINGS.ROGUE) then

				MACAROON_STRINGS.STATES.stance0 = MACAROON_STRINGS.ROGUE_ATTACK

			elseif (UnitClass("player") == MACAROON_STRINGS.WARLOCK) then

				MACAROON_STRINGS.STATES.stance0 = MACAROON_STRINGS.WARLOCK_CASTER

			elseif (UnitClass("player") == MACAROON_STRINGS.WARRIOR) then

				MACAROON_STRINGS.STATES.stance0 = nil
				Macaroon.managedStates.stance.homestate = "stance1"
			end

			specialStringsUpdated = true
		end
	else

		MACAROON_STRINGS.BARSTATE_2 = "exclude"
		MACAROON_STRINGS.STATES.stance0 = nil
	end
end

local function updateCompanionData()

	local creatureID, creatureName, spellID, icon, active

	for i=1,GetNumCompanions("CRITTER") do
		creatureID, creatureName, spellID, icon, active = GetCompanionInfo("CRITTER", i)
		if (creatureName) then
			Macaroon.companionIndex[lower(creatureName)] = { "CRITTER", i, creatureID, creatureName, spellID, icon }
			Macaroon.companionIndex[lower(creatureName).."()"] = { "CRITTER", i, creatureID, creatureName, spellID, icon }
		end
	end

	for i=1,GetNumCompanions("MOUNT") do
		creatureID, creatureName, spellID, icon, active = GetCompanionInfo("MOUNT", i)
		if (creatureName) then
			Macaroon.companionIndex[lower(creatureName)] = { "CRITTER", i, creatureID, creatureName, spellID, icon }
			Macaroon.companionIndex[lower(creatureName).."()"] = { "MOUNT", i, creatureID, creatureName, spellID, icon }
		end
	end
end

local function printSlashHelp()

	DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.SLASH_HINT1)
	DEFAULT_CHAT_FRAME:AddMessage(MACAROON_STRINGS.SLASH_HINT2)

	for k,v in ipairs(Macaroon.SlashHelp) do
		DEFAULT_CHAT_FRAME:AddMessage(v)
	end
end

local function slashHandler(msg)

	local commands = {}

	if ((not msg) or (strlen(msg) <= 0)) then

		printSlashHelp()

		return
	end

	gsub(msg, "(%S+)", function(cmd) tinsert(commands, cmd) end)

	if (Macaroon.SlashCommands[commands[1]]) then

		local command

		for k,v in ipairs(commands) do
			if (k ~= 1) then
				if (not command) then
					command = v
				else
					command = command.." "..v
				end
			end
		end

		if (commands) then
			Macaroon.SlashCommands[commands[1]][2](command)
		end
	else
		printSlashHelp()
	end

end

function macUpdateContainerFrameAnchors()

	if (ss.checkButtons[108]) then

		if (not ss.bagScale) then
			ss.bagScale = 1
		end

		local frame, xOffset, yOffset, screenHeight, freeScreenHeight, leftMostPoint, column;
		local screenWidth = GetScreenWidth()
		local bagScale = 1;
		local leftLimit = 0;

		if ( BankFrame:IsShown() ) then
			leftLimit = BankFrame:GetRight() - 25;
		end

		if (ss.bagScale == 1) then

			while ( bagScale > CONTAINER_SCALE ) do
				screenHeight = GetScreenHeight() / bagScale;
				-- Adjust the start anchor for bags depending on the multibars
				xOffset = CONTAINER_OFFSET_X / bagScale;
				yOffset = CONTAINER_OFFSET_Y / bagScale;
				-- freeScreenHeight determines when to start a new column of bags
				freeScreenHeight = screenHeight - yOffset;
				leftMostPoint = screenWidth - xOffset;
				column = 1;
				local frameHeight;
				for index, frameName in ipairs(ContainerFrame1.bags) do
					frameHeight = _G[frameName]:GetHeight()
					if ( freeScreenHeight < frameHeight ) then
						-- Start a new column
						column = column + 1;
						leftMostPoint = screenWidth - ( column * CONTAINER_WIDTH * bagScale ) - xOffset;
						freeScreenHeight = screenHeight - yOffset;
					end
					freeScreenHeight = freeScreenHeight - frameHeight - VISIBLE_CONTAINER_SPACING;
				end
				if ( leftMostPoint < leftLimit ) then
					bagScale = bagScale - 0.01;
				else
					break;
				end
			end

			if ( bagScale < CONTAINER_SCALE ) then
				bagScale = CONTAINER_SCALE;
			end
		else
			bagScale = ss.bagScale
		end

		screenHeight = GetScreenHeight() / bagScale;
		xOffset = ss.bagOffsetX / bagScale;
		yOffset = ss.bagOffsetY / bagScale;
		freeScreenHeight = screenHeight - yOffset;
		column = 0;

		for index, frameName in ipairs(ContainerFrame1.bags) do
			frame = _G[frameName]
			frame:SetScale(bagScale)
			frame:ClearAllPoints()
			if ( index == 1 ) then
				-- First bag
				frame:SetPoint("BOTTOMRIGHT", frame:GetParent(), "BOTTOMRIGHT", -xOffset, yOffset )
			elseif ( freeScreenHeight < frame:GetHeight() ) then
				-- Start a new column
				column = column + 1;
				freeScreenHeight = screenHeight - yOffset;
				frame:SetPoint("BOTTOMRIGHT", frame:GetParent(), "BOTTOMRIGHT", -(column * CONTAINER_WIDTH) - xOffset, yOffset )
			else
				-- Anchor to the previous bag
				frame:SetPoint("BOTTOMRIGHT", ContainerFrame1.bags[index - 1], "TOPRIGHT", 0, CONTAINER_SPACING)
			end
			freeScreenHeight = freeScreenHeight - frame:GetHeight() - VISIBLE_CONTAINER_SPACING;
		end
	end
end

Macaroon.CheckbuttonOptions = {
	[101] = function(self)
			if (ss.checkButtons[self:GetID()]) then

				MainMenuBar_OnLoad(MainMenuBar)
				MainMenuBar:Show()

				MainMenuBar_OnLoad(MainMenuBarArtFrame)
				MainMenuBarArtFrame:Show()

				if (GetNumShapeshiftForms() > 0) then
					ShapeshiftBar_OnLoad(ShapeshiftBarFrame)
					ShapeshiftBarFrame:Show()
				else
					ShapeshiftBarFrame:UnregisterAllEvents()
					ShapeshiftBarFrame:Hide()
				end

				BonusActionBar_OnLoad(BonusActionBarFrame)
				BonusActionBarFrame:Show()

				PossessBar_OnLoad(PossessBarFrame)
				PossessBarFrame:Show()

				HideBonusActionBar()

			else
				MainMenuBar:UnregisterAllEvents()
				MainMenuBar:Hide()

				MainMenuBarArtFrame:UnregisterAllEvents()
				MainMenuBarArtFrame:RegisterEvent("KNOWN_CURRENCY_TYPES_UPDATE")
				MainMenuBarArtFrame:RegisterEvent("CURRENCY_DISPLAY_UPDATE")
				MainMenuBarArtFrame:Hide()

				ShapeshiftBarFrame:UnregisterAllEvents()
				ShapeshiftBarFrame:Hide()

				BonusActionBarFrame:UnregisterAllEvents()
				BonusActionBarFrame:Hide()

				PossessBarFrame:UnregisterAllEvents()
				PossessBarFrame:Hide()
			end
		end,
	[102] = function(self) end,
	[103] = function(self) end,
	[104] = function(self)
			if (ss.checkButtons[self:GetID()]) then
				for k,v in pairs(Macaroon.Buttons) do
					v[1].hotkey:Show()
				end
			else
				for k,v in pairs(Macaroon.Buttons) do
					v[1].hotkey:Hide()
				end
			end
		end,
	[105] = function(self)
			if (ss.checkButtons[self:GetID()]) then
				for k,v in pairs(Macaroon.Buttons) do
					v[1].name:Show()
				end
			else
				for k,v in pairs(Macaroon.Buttons) do
					v[1].name:Hide()
				end
			end
		end,
	[106] = function(self)
			if (ss.checkButtons[self:GetID()]) then
				for k,v in pairs(Macaroon.Buttons) do
					v[1].count:Show()
				end
			else
				for k,v in pairs(Macaroon.Buttons) do
					v[1].count:Hide()
				end
			end
		end,
	[107] = function(self) end,
	[108] = function(self) end,
	[109] = function(self) self:SetChecked(ss.checkButtons[109]) end,
	[110] = function(self) end,
	[111] = function(self) end,
	[112] = function(self) end,
	[113] = function(self) end,
	[114] = function(self) end,
	[115] = function(self)

			local onevent

			for k,v in pairs(Macaroon.Buttons) do
				if (v[1].config.type ~= "pet") then
					onevent = v[1]:GetScript("OnEvent")
					onevent(v[1], "UNIT_AURA", "player")
					onevent(v[1], "UNIT_AURA", "target")
				end
			end
	end,
	[116] = function(self)
			if (ss.checkButtons[self:GetID()]) then
				ss.registerForClicks = "AnyDown"
			else
				ss.registerForClicks = "AnyUp"
			end

			for k,v in pairs(Macaroon.Buttons) do
				v[1].updateClicks(v[1])
			end
	end,
	[117] = function(self) end,
	[201] = function(self)
			if (ss.checkButtons[self:GetID()]) then
				MacaroonMinimapButton:Show()
			else
				MacaroonMinimapButton:Hide()
			end
	end,
	[202] = function(self) end,
	[203] = function(self) end,
	[301] = function(self)
			local button = Macaroon.CurrentButton
			if (button) then

				if (self:GetChecked()) then
					button.config.clickAnchor = true
					button.config.mouseAnchor = false
					self.mouseanchor:SetChecked(nil)
				else
					button.config.clickAnchor = false
					button.config.mouseAnchor = false
				end

				self.anchorchild:SetText("-none-")
				self.delay:SetText("-none-")

				Macaroon.SetAnchor(button, self:GetChecked())
			end
	end,
	[302] = function(self)
			local button = Macaroon.CurrentButton
			if (button) then

				if (self:GetChecked()) then
					button.config.mouseAnchor = true
					button.config.clickAnchor = false
					self.clickanchor:SetChecked(nil)
				else
					button.config.mouseAnchor = false
					button.config.clickAnchor = false
				end

				self.anchorchild:SetText("-none-")
				self.delay:SetText("-none-")

				Macaroon.SetAnchor(button, self:GetChecked())
			end
	end,
}

local sliderMinMax = {
	[1] = { min = 0, max = GetScreenWidth()-ContainerFrame1:GetWidth()*ContainerFrame1:GetEffectiveScale() },
	[2] = { min = 0, max = GetScreenHeight()-ContainerFrame1:GetHeight()*ContainerFrame1:GetEffectiveScale() },
	[3] = { min = 0.2, max = 2 },
	[4] = { min = 1, max = 100 },
	[5] = { min = 0, max = 1 },
	[6] = { min = 0, max = 1 },
	[7] = { min = 0.005, max = 0.5 },
	[8] = { min = 0, max = 100 },
}

local sliderOnShow = {
	[1] = function(self)
			self:SetValue(ss.bagOffsetX)
			if (self.editbox) then
				self.editbox:SetText(format("%.1f",ss.bagOffsetX))
			end
	end,
	[2] = function(self)
			self:SetValue(ss.bagOffsetY)
			if (self.editbox) then
				self.editbox:SetText(format("%.1f",ss.bagOffsetY))
			end
	end,
	[3] = function(self)
			self:SetValue(ss.bagScale)
			if (self.editbox) then
				self.editbox:SetText(format("%.1f",ss.bagScale))
			end
	end,
	[4] = function(self)
			self:SetValue(ss.freeSlots)
			if (self.editbox) then
				self.editbox:SetText(format("%.0f",ss.freeSlots))
			end

			local totalSlots, bagFamily, _ = 0

			for i=BACKPACK_CONTAINER, NUM_BAG_SLOTS do
				_, bagFamily = GetContainerNumFreeSlots(i)
				if (bagFamily == 0) then
					totalSlots = totalSlots + GetContainerNumSlots(i)
				end
			end

			self:SetMinMaxValues(0,totalSlots)
	end,
	[5] = function(self)
			self:SetValue(ss.cooldownAlpha)
			if (self.editbox) then
				self.editbox:SetText(format("%.2f",ss.cooldownAlpha))
			end
	end,
	[6] = function(self)
			self:SetValue(ss.autocastAlpha)
			if (self.editbox) then
				self.editbox:SetText(format("%.2f",ss.autocastAlpha))
			end
	end,
	[7] = function(self)
			self:SetValue(ss.fadeSpeed)
			if (self.editbox) then
				self.editbox:SetText(format("%.0f",ss.fadeSpeed*200))
			end
	end,
	[8] = function(self)
			self:SetValue(ss.snapToTol)
			if (self.editbox) then
				self.editbox:SetText(format("%.0f",ss.snapToTol))
			end
	end,
}

local sliderOnValueChanged = {
	[1] = function(self)
			ss.bagOffsetX = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.1f",ss.bagOffsetX))
			end
			updateContainerFrameAnchors()
	end,
	[2] = function(self)
			ss.bagOffsetY = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.1f",ss.bagOffsetY))
			end
			updateContainerFrameAnchors()
	end,
	[3] = function(self)
			ss.bagScale = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.1f",ss.bagScale))
			end
			updateContainerFrameAnchors()
	end,
	[4] = function(self)
			ss.freeSlots = floor(self:GetValue())
			if (self.editbox) then
				self.editbox:SetText(format("%.0f",ss.freeSlots))
			end
			if (Macaroon.MacaroonBackpackButton_OnEvent) then
				Macaroon.MacaroonBackpackButton_OnEvent(MacaroonBackpackButton, "PLAYER_ENTERING_WORLD")
			end
	end,
	[5] = function(self)
			ss.cooldownAlpha = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.2f",ss.cooldownAlpha))
			end
	end,
	[6] = function(self)
			ss.autocastAlpha = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.2f",ss.autocastAlpha))
			end

			local onevent

			for k,v in pairs(Macaroon.Buttons) do
				if (v[1].config.type == "pet") then
					onevent = v[1]:GetScript("OnEvent")
					onevent(v[1], "PET_BAR_UPDATE", "player")
				end
			end
	end,
	[7] = function(self)
			ss.fadeSpeed = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.0f",ss.fadeSpeed*200))
			end
	end,
	[8] = function(self)
			ss.snapToTol = self:GetValue()
			if (self.editbox) then
				self.editbox:SetText(format("%.0f",ss.snapToTol))
			end
	end,
}

function Macaroon.OpenMainMenu()
	if (MacaroonMainMenu:IsVisible()) then
		InterfaceOptionsFrameOkay_OnClick()
	else
		InterfaceOptionsFrame_OpenToCategory(MacaroonMainMenu)
	end
end

function Macaroon.ButtonLock()

	local frame = MacaroonMainMenuCheck109

	if ( frame:GetChecked() ) then
		frame:SetChecked(nil)
		PlaySound("igMainMenuOptionCheckBoxOff")
	else
		frame:SetChecked(1)
		PlaySound("igMainMenuOptionCheckBoxOn")
	end

	ss.checkButtons[109] = frame:GetChecked()

	Macaroon.CheckbuttonOptions[109](frame)
end

function Macaroon.LoadFromProfile(name)

	if (not IsAddOnLoaded("MacaroonProfiles")) then
		LoadAddOn("MacaroonProfiles")
	end

	Macaroon.LoadProfile(name)
end

function Macaroon.EditBox_PopUpInitialize(popupFrame, data, configFrame)

	popupFrame.func = Macaroon.PopUp_Update
	popupFrame.data = data

	Macaroon.PopUp_Update(popupFrame, configFrame)
end

function Macaroon.PopUp_Update(popupFrame, configFrame)

	local data, columns, optionText = popupFrame.data, 1
	local count, height, width, widthMult, option, lastOption, lastAnchor = 1,0, popupFrame:GetParent():GetWidth(), 1, nil, nil, nil

	if (popupFrame.options) then
		for k,v in pairs(popupFrame.options) do
			v:Hide()
		end
	end

	popupFrame.array = {}

	if (not data) then
		return
	end

	for k,v in pairs(data) do

		if (type(v) == "string") then
			popupFrame.array[count] = k..","..v
		else
			popupFrame.array[count] = k
		end

		count = count + 1
	end

	table.sort(popupFrame.array)

	count = 1

	columns = (math.ceil(#popupFrame.array/20)) or 1

	for i=1,#popupFrame.array do

		popupFrame.array[i] = gsub(popupFrame.array[i], "%s+", " ")
		popupFrame.array[i] = gsub(popupFrame.array[i], "^%s+", "")

		if (not popupFrame.options[i]) then
			option = CreateFrame("Button", popupFrame:GetName().."Option"..i, popupFrame, "MacaroonButtonTemplate3")
			option:SetHeight(14)

			popupFrame.options[i] = option
		else
			option = _G[popupFrame:GetName().."Option"..i]
			popupFrame.options[i] = option
		end

		optionText = _G[option:GetName().."Text"]

		optionText:SetText(match(popupFrame.array[i], "^[^,]+"))

		option.value = match(popupFrame.array[i], "[^,]+$")

		if (optionText:GetWidth()+20 > width and optionText:GetWidth()+20 < 250) then
			width = _G[option:GetName().."Text"]:GetWidth() + 20
		end

		option:ClearAllPoints()

		if (count == 1) then
			if (lastAnchor) then
				option:SetPoint("LEFT", lastAnchor, "RIGHT", 0, 0)
				lastOption = option
				lastAnchor = option

			else
				option:SetPoint("TOPLEFT", popupFrame, "TOPLEFT", 0, -5)
				lastOption = option
				lastAnchor = option
			end
		else
			option:SetPoint("TOP", lastOption, "BOTTOM", 0, -1)
			lastOption = option
		end

		if (widthMult == 1) then
			height = height + 15
		end

		count = count + 1

		if (count > math.ceil(#popupFrame.array/columns) and widthMult < columns and columns > 1) then
			widthMult = widthMult + 1
			count = 1
		end

		option:Show()
	end

	if (popupFrame.options) then
		for k,v in pairs(popupFrame.options) do
			v:SetWidth(width)
		end
	end

	popupFrame:SetWidth(width * widthMult)

	if (popupFrame:GetParent():GetHeight() > height + 10) then
		popupFrame:SetHeight(popupFrame:GetParent():GetHeight())
	else
		popupFrame:SetHeight(height + 10)
	end
end

local function selfCast_OnShow(self)

	if (ss.selfCast) then
		self:SetText(ss.selfCast)
	else
		self:SetText("-none-")
	end

	local data = {
		["-none-"] = false,
		["Alt Cast"] = "alt",
		["Ctrl Cast"] = "ctrl",
		["Shift Cast"] = "shift",
	}

	Macaroon.EditBox_PopUpInitialize(self.popup, data)
end

local function selfCast_OnTextChanged(self)

	if (self:GetText() == "-none-") then
		ss.selfCast = false
	else
		ss.selfCast = self:GetText()

		for k,v in pairs(Macaroon.BarIndex) do
			v.updateBar(v, true)
		end
	end

	Macaroon.UpdateAutoMacros()

end

local function focusCast_OnShow(self)

	if (ss.focusCast) then
		self:SetText(ss.focusCast)
	else
		self:SetText("-none-")
	end

	local data = {
		["-none-"] = false,
		["Alt Cast"] = "alt",
		["Ctrl Cast"] = "ctrl",
		["Shift Cast"] = "shift",
	}

	Macaroon.EditBox_PopUpInitialize(self.popup, data)
end

local function focusCast_OnTextChanged(self)

	if (self:GetText() == "-none-") then
		ss.focusCast = false
	else
		ss.focusCast = self:GetText()

		for k,v in pairs(Macaroon.BarIndex) do
			v.updateBar(v, true)
		end
	end

	Macaroon.UpdateAutoMacros()

end

local function rightClick_OnShow(self)

	local data = {
		["-none-"] = false,
		["Self"] = "player",
		["Pet"] = "pet",
		["Target"] = "target",
		["Target of Target"] = "targettarget",
		["Focus"] = "focus",
		["Target of Focus"] = "focustarget",
	}

	if (ss.rightClickTarget) then

		for k,v in pairs(data) do
			if (v==ss.rightClickTarget) then
				self:SetText(k)
			end
		end
	else
		self:SetText("-none-")
	end

	Macaroon.EditBox_PopUpInitialize(self.popup, data)
end

local function rightClick_OnTextChanged(self)

	if (self:GetText() == "-none-") then

		ss.rightClickTarget = false

	else
		if (self.value) then
			ss.rightClickTarget = self.value
		end

		for k,v in pairs(Macaroon.BarIndex) do
			v.updateBar(v, true)
		end
	end

	Macaroon.UpdateAutoMacros()

end

function Macaroon.MacaroonMainMenu_BuildOptions(self)

	local index, breakIndex, xOffset, frame, lastFrame = 1, 9, 15

	frame = CreateFrame("EditBox", "$parentSelfCast", self, "MacaroonEditBoxTemplate1")
	frame:SetWidth(103)
	frame:SetHeight(20)
	frame:SetTextInsets(7,3,0,0)
	frame.text:SetText("Self Cast Option:")
	frame:SetPoint("TOPLEFT", "$parentDropdownBorder", "TOPLEFT", 110, -10)
	frame:SetScript("OnTextChanged", selfCast_OnTextChanged)
	frame:SetScript("OnShow", selfCast_OnShow)
	self.selfcast = frame

	frame = CreateFrame("EditBox", "$parentFocusCast", self, "MacaroonEditBoxTemplate1")
	frame:SetWidth(103)
	frame:SetHeight(20)
	frame:SetTextInsets(7,3,0,0)
	frame.text:SetText("Focus Cast Option:")
	frame:SetPoint("TOP", "$parentSelfCast", "BOTTOM", 0, -3)
	frame:SetScript("OnTextChanged", focusCast_OnTextChanged)
	frame:SetScript("OnShow", focusCast_OnShow)
	self.focuscast = frame

	frame = CreateFrame("EditBox", "$parentRightClick", self, "MacaroonEditBoxTemplate1")
	frame:SetWidth(103)
	frame:SetHeight(20)
	frame:SetTextInsets(7,3,0,0)
	frame.text:SetText("Right Click Target:")
	frame:SetPoint("TOP", "$parentFocusCast", "BOTTOM", 0, -3)
	frame:SetScript("OnTextChanged", rightClick_OnTextChanged)
	frame:SetScript("OnShow", rightClick_OnShow)
	self.rightclick = frame

	frame = CreateFrame("CheckButton", "$parentCheck201", self, "MacaroonOptionCBTemplate")
	frame:SetID(201)
	frame.text:SetText(MACAROON_STRINGS.MINIMAP_CHECK)
	frame:SetPoint("LEFT", "$parentSelfCast", "RIGHT", 35, 0)

	frame = CreateFrame("CheckButton", "$parentCheck202", self, "MacaroonOptionCBTemplate")
	frame:SetID(202)
	frame.text:SetText(MACAROON_STRINGS.FLOAT_EDITORS)
	frame:SetPoint("LEFT", "$parentFocusCast", "RIGHT", 35, 0)

	frame = CreateFrame("CheckButton", "$parentCheck203", self, "MacaroonOptionCBTemplate")
	frame:SetID(203)
	frame.text:SetText(MACAROON_STRINGS.ANIMATED)
	frame:SetPoint("LEFT", "$parentRightClick", "RIGHT", 35, 0)

	local top, bottom , _, yOffset, yOfs

	frame = _G[self:GetName().."CheckbuttonBorder"]

	for i=1,frame:GetNumPoints() do
		_, _, _, _, yOfs = frame:GetPoint(i)

		if (not top or yOfs > top) then
			top = abs(yOfs)
		end

		if (not bottom or yOfs < bottom) then
			bottom = abs(yOfs)
		end
	end

	yOffset = ((top+bottom-20)-(breakIndex*22))/breakIndex

	while (MACAROON_STRINGS["CHECK_"..index+100]) do

		frame = CreateFrame("CheckButton", "$parentCheck"..index+100, self, "MacaroonOptionCBTemplate")
		frame:SetID(index+100)

		frame.text:SetText(MACAROON_STRINGS["CHECK_"..index+100])

		if (index == breakIndex) then
			xOffset = xOffset + 185
		end

		if (index == 1 or index == breakIndex) then
			frame:SetPoint("TOPLEFT", "$parentCheckbuttonBorder", "TOPLEFT", xOffset, -10)
			lastFrame = frame
		else
			frame:SetPoint("TOP", lastFrame, "BOTTOM", 0, -(yOffset))
			lastFrame = frame
		end

		index = index + 1
	end

	index, breakIndex, xOffset = 1, 5, 10

	while (MACAROON_STRINGS["SLIDER_"..index]) do

		frame = CreateFrame("Slider", "$parentSlider"..index, self, "MacaroonSliderTemplate1")
		frame:SetID(index)
		frame:SetWidth(145)
		frame:SetMinMaxValues(sliderMinMax[index].min, sliderMinMax[index].max)
		frame.onshow_func = sliderOnShow[index]
		frame.onvaluechanged_func = sliderOnValueChanged[index]

		if (index == 4) then
			frame:Disable()
			frame.text2:SetTextColor(0.5,0.5,0.5)
		end

		if (index == breakIndex) then
			xOffset = xOffset + 190
		end

		if (index == 1 or index == breakIndex) then
			frame:SetPoint("TOPLEFT", "$parentSliderBorder", "TOPLEFT", xOffset, -18)
			lastFrame = frame
		else
			frame:SetPoint("TOP", lastFrame, "BOTTOM", 0, -10)
			lastFrame = frame
		end

		frame = CreateFrame("EditBox", "$parentSliderEdit"..index, self, "MacaroonEditBoxTemplate3")
		frame:SetID(index)
		frame:SetPoint("LEFT", lastFrame, "RIGHT", 1, 0)
		frame.slider = lastFrame
		frame:SetScript("OnTabPressed", function(self) local num = tonumber(self:GetText())/200  if(num) then self.slider:SetValue(num) end self:ClearFocus() end)
		frame:SetScript("OnEnterPressed", function(self) local num = tonumber(self:GetText())/200  if(num) then self.slider:SetValue(num) end self:ClearFocus() end)

		lastFrame.editbox = frame

		index = index + 1

	end
end

function Macaroon.CheckButtonOptions_OnClick(self)

	if ( self:GetChecked() ) then
		PlaySound("igMainMenuOptionCheckBoxOn")
	else
		PlaySound("igMainMenuOptionCheckBoxOff")
	end

	ss.checkButtons[self:GetID()] = self:GetChecked()

	Macaroon.CheckbuttonOptions[self:GetID()](self)
end

function Macaroon.CheckButtonOptions_OnShow(self)

	if (ss) then
		self:SetChecked(ss.checkButtons[self:GetID()])
	end
end

function Macaroon.SliderOptions_OnShow(self)

end

function Macaroon.SliderOptions_OnValueChanged(self, value)

end

--From wowwiki.com
local minimapShapes = {

	-- quadrant booleans (same order as SetTexCoord)
	-- {upper-left, lower-left, upper-right, lower-right}
	-- true = rounded, false = squared

	["ROUND"] 			= {true, true, true, true},
	["SQUARE"] 			= {false, false, false, false},
	["CORNER-TOPLEFT"] 		= {true, false, false, false},
	["CORNER-TOPRIGHT"] 		= {false, false, true, false},
	["CORNER-BOTTOMLEFT"] 		= {false, true, false, false},
	["CORNER-BOTTOMRIGHT"]	 	= {false, false, false, true},
	["SIDE-LEFT"] 			= {true, true, false, false},
	["SIDE-RIGHT"] 			= {false, false, true, true},
	["SIDE-TOP"] 			= {true, false, true, false},
	["SIDE-BOTTOM"] 		= {false, true, false, true},
	["TRICORNER-TOPLEFT"] 		= {true, true, true, false},
	["TRICORNER-TOPRIGHT"] 		= {true, false, true, true},
	["TRICORNER-BOTTOMLEFT"] 	= {true, true, false, true},
	["TRICORNER-BOTTOMRIGHT"] 	= {false, true, true, true},
}

function Macaroon.DragFrame_OnUpdate(x, y)

	local pos, quad, round, radius = nil, nil, nil, ss.buttonRadius - MacaroonMinimapButton:GetWidth()/math.pi
	local sqRad = sqrt(2*(radius)^2)

	local xmin, ymin = Minimap:GetLeft(), Minimap:GetBottom()

	local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
	local quadTable = minimapShapes[minimapShape]

	local xpos, ypos = x, y

	if (not xpos or not ypos) then
		xpos, ypos = GetCursorPosition()
	end

	xpos = xmin - xpos / Minimap:GetEffectiveScale() + radius
	ypos = ypos / Minimap:GetEffectiveScale() - ymin - radius

	pos = math.deg(math.atan2(ypos,xpos))

	xpos = cos(pos)
	ypos = sin(pos)

	if (xpos > 0 and ypos > 0) then
		quad = 1 --topleft
	elseif (xpos > 0 and ypos < 0) then
		quad = 2 --bottomleft
	elseif (xpos < 0 and ypos > 0) then
		quad = 3 --topright
	elseif (xpos < 0 and ypos < 0) then
		quad = 4 --bottomright
	end

	round = quadTable[quad]

	if (round) then
		xpos = xpos * radius
		ypos = ypos * radius
	else
		xpos = max(-radius, min(xpos * sqRad, radius))
		ypos = max(-radius, min(ypos * sqRad, radius))
	end

	MacaroonMinimapButton:SetPoint("TOPLEFT", "Minimap", "TOPLEFT", 52-xpos, ypos-55)

	ss.buttonLoc = { 52-xpos, ypos-55 }
end

function Macaroon.MinimapButton_OnLoad(self)

	local data, index = {}, 1

	while (MACAROON_STRINGS["MINIMAP_ACTION_"..index]) do
		data[MACAROON_STRINGS["MINIMAP_ACTION_"..index]] = tostring(index)
		index = index + 1
	end

	self:RegisterForClicks("AnyUp")
	self:RegisterForDrag("LeftButton")
	self:RegisterEvent("PLAYER_LOGIN")
	self.elapsed = 0
	self.x = 0
	self.y = 0
	self.count = 1
	self.angle = 0
	self.popup = _G[self:GetName().."PopUp"]
	self.icon = _G[self:GetName().."Icon"]
	--self.icon:SetTexCoord(self.x, self.x+0.125, self.y, self.y+0.25)
	self:SetFrameStrata(MinimapCluster:GetFrameStrata())
	self:SetFrameLevel(MinimapCluster:GetFrameLevel()+3)

	Macaroon.EditBox_PopUpInitialize(self.popup, data)
end

function Macaroon.MinimapButton_OnEvent(self)

	Macaroon.MinimapButton_OnDragStop(self)

end

function Macaroon.MinimapButton_OnDragStart(self)

	self:LockHighlight()
	self:StartMoving()
	MacaroonMinimapButtonDragFrame:Show()
end

function Macaroon.MinimapButton_OnDragStop(self)

	self:UnlockHighlight()
	self:StopMovingOrSizing()
	self:SetUserPlaced(false)
	self:ClearAllPoints()
	if (ss) then
		self:SetPoint("TOPLEFT", "Minimap","TOPLEFT", ss.buttonLoc[1], ss.buttonLoc[2])
	end
	MacaroonMinimapButtonDragFrame:Hide()
end

function Macaroon.MinimapButton_OnShow(self)

	if (ss) then
		Macaroon.MinimapButton_OnDragStop(self)
	end
end

function Macaroon.MinimapButton_OnHide(self)

	self:UnlockHighlight()
	MacaroonMinimapButtonDragFrame:Hide()
end

function Macaroon.MinimapButton_OnEnter(self)

	local status

	GameTooltip_SetDefaultAnchor(GameTooltip, UIParent)

	GameTooltip:SetText(MACAROON_STRINGS.MINIMAP_TOOLTIP0)

	GameTooltip:AddLine(MACAROON_STRINGS.MINIMAP_TOOLTIP1)

	if (ss.checkButtons[109]) then
		GameTooltip:AddLine(MACAROON_STRINGS.MINIMAP_TOOLTIP2.."|cff00ff00"..MACAROON_STRINGS.BARTOOLTIP_1.."|r")
	else
		GameTooltip:AddLine(MACAROON_STRINGS.MINIMAP_TOOLTIP2.."|cfff00000"..MACAROON_STRINGS.BARTOOLTIP_2.."|r")
	end

	GameTooltip:Show()
end

function Macaroon.MinimapButton_OnLeave(self)

	GameTooltip:Hide()
end

function Macaroon.MinimapButton_OnUpdate(self, elapsed)

	self.elapsed = self.elapsed + elapsed

	if (self.elapsed > 10) then
		self.blink = true
	end

	if (false) then

		if (self.elapsed > 0.05) then

			self.icon:SetRotation(self.angle)

			self.angle = self.angle + 0.1

			if (self.angle > 5.5) then
				self.angle = 1
			end

			--[[
			self.x = self.x + 0.125

			if (self.x > 0.875) then
				self.x = 0
				self.y = self.y + 0.25
			end

			if (self.x >= 0.625 and self.y >= 0.75) then
				self.x=0; self.y=0; self.blink = nil; self.count = 0
			end

			self.icon:SetTexCoord(self.x, self.x+0.125, self.y, self.y+0.25)

			self.count = self.count + 1

			self.elapsed = 0
			]]--

			self.elapsed = 0
		end
	end

end

function Macaroon.MinimapButton_OnClick(self, button)

	PlaySound("igChatScrollDown")

	if (InCombatLockdown()) then return end

	if (button == "RightButton") then

		if (self.popup:IsVisible()) then
			self.popup:Hide()
		elseif (self.popup.data) then
			self.popup:Show()
		end

	elseif (IsShiftKeyDown()) then

		Macaroon.ButtonBind()

	elseif (IsAltKeyDown()) then

		Macaroon.ButtonEdit()

	elseif (IsControlKeyDown()) then

		Macaroon.OpenMainMenu()

	else
		Macaroon.ConfigBars()
	end
end

function Macaroon.OptionsSlider_OnShow(self)

	if (self.onshow_func) then
		self.onshow_func(self)
	end
end

function Macaroon.OptionsSlider_OnValueChanged(self, value)

	if (self.onvaluechanged_func) then
		self.onvaluechanged_func(self, value)
	end
end

function Macaroon.PanelMover_OnDragStart(self)

	MacaroonPanelMover:Show()

	MacaroonPanelMover:ClearAllPoints()

	MacaroonPanelMover:SetUserPlaced(true)

	MacaroonPanelMover:StartMoving()
end

function Macaroon.PanelMover_OnDragStop(self)

	MacaroonPanelMover:Hide()

	MacaroonPanelMover:StopMovingOrSizing()
end

function Macaroon.StorageCreateButton()

	PlaySound("gsTitleOptionExit")

	local button = Macaroon.AddNewButton(MacaroonButtonStorage)

	Macaroon.StoreButton(button, Macaroon.Buttons)

	Macaroon.UpdateButtonStorage()

end

function Macaroon.StorageDeleteButton()

	PlaySound("gsTitleOptionExit")

	local lastButton, found = 0

	for k,v in pairs(Macaroon.Buttons) do
		if (v[2] == 1) then
			if (k > lastButton) then
				lastButton = k; found = true
			end
		end
	end

	if (found) then

		Macaroon.Buttons[lastButton] = nil

		lastButton = _G["MacaroonButton"..lastButton]
		lastButton.config = nil

		lastButton:ClearAllPoints()
		lastButton:SetParent("UIParent")
		lastButton:Hide()

		Macaroon.SaveCurrentState()

		Macaroon.UpdateButtonStorage()
	end
end

local function buttonsSetSaved()

	if (MacaroonSavedState.useShared and MacaroonSharedSavedState[MacaroonSavedState.sharedProfile]) then

		ss = MacaroonSharedSavedState[MacaroonSavedState.sharedProfile]

		if (not ss) then
			ss = MacaroonSavedState
		end
	else
		ss = MacaroonSavedState
	end
end

function Macaroon.Control_OnEvent(self, event, ...)

	if (event == "VARIABLES_LOADED") then

		buttonsSetSaved()

		for k,v in pairs(defaultSavedState) do

			if (ss[k] == nil) then
				ss[k] = v
			end
		end

		updateShapeshiftStrings()
	end

	if (event == "PLAYER_LOGIN") then

		local index, button, texture = 1

		SlashCmdList["MACAROON"] = slashHandler
		SLASH_MACAROON1 = MACAROON_STRINGS.SLASH1
		SLASH_MACAROON2 = MACAROON_STRINGS.SLASH2

		while (MACAROON_STRINGS["SLASH_COMMAND_"..index]) do

			local command = MACAROON_STRINGS["SLASH_COMMAND_"..index]
			local desc = MACAROON_STRINGS["SLASH_COMMAND_"..index.."_DESC"]
			local func = Macaroon[MACAROON_STRINGS["SLASH_COMMAND_"..index.."_FUNC"]]

			Macaroon.SlashCommands[command] = { desc, func }
			Macaroon.SlashHelp[index] = "       |cff00ff00"..command.."|r: "..desc
			index = index + 1
		end

		hooksecurefunc("updateContainerFrameAnchors", macUpdateContainerFrameAnchors)
	end

	if (event == "PLAYER_ENTERING_WORLD") then

		if (not playerEnteredWorld) then
			for k,v in pairs(Macaroon.CheckbuttonOptions) do
				v(_G["MacaroonMainMenuCheck"..k])
			end

			collectgarbage()
		end

		updateSpellIndex()
		updateMacroIconIndex()
		updateCompanionData()

		playerEnteredWorld = true

		for k,v in pairs(Macaroon.StatesToSave) do v() end

		ss.savedVersion = currentVersion
	end

	if (event == "SKILL_LINES_CHANGED") then
		updateSpellIndex(true)
		updateShapeshiftStrings()
	end

	if (event == "LEARNED_SPELL_IN_TAB" or event == "CHARACTER_POINTS_CHANGED") then
		updateSpellIndex()
		updateShapeshiftStrings()
	end

	if (event == "PET_UI_CLOSE" or event == "COMPANION_LEARNED" or event == "COMPANION_UPDATE") then
		updateCompanionData()
	end

	if (event == "PLAYER_REGEN_DISABLED") then
		Macaroon.ConfigBars(true)
		Macaroon.ButtonEdit(true)
		Macaroon.ButtonBind(true)
	end
end

function Macaroon.Control_OnUpdate(self, elapsed)

	self.elapsed = self.elapsed + elapsed

end

local frame = CreateFrame("Frame", "MacaroonControl", UIParent)

frame.elapsed = 0
frame:SetScript("OnEvent", Macaroon.Control_OnEvent)
frame:SetScript("OnUpdate", Macaroon.Control_OnUpdate)

frame:RegisterEvent("VARIABLES_LOADED")
frame:RegisterEvent("PLAYER_LOGIN")
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("SKILL_LINES_CHANGED")
frame:RegisterEvent("CHARACTER_POINTS_CHANGED")
frame:RegisterEvent("LEARNED_SPELL_IN_TAB")
frame:RegisterEvent("CURSOR_UPDATE")
frame:RegisterEvent("PET_UI_CLOSE")
frame:RegisterEvent("COMPANION_LEARNED")
frame:RegisterEvent("COMPANION_UPDATE")
frame:RegisterEvent("PLAYER_REGEN_DISABLED")

frame = CreateFrame("GameTooltip", "MacaroonTooltipScan", UIParent, "GameTooltipTemplate")
frame:SetOwner(UIParent, "ANCHOR_NONE")
frame:SetFrameStrata("TOOLTIP")
frame:Hide()